﻿#include "animation_clip.h"
// Inner Fire 游戏引擎库
// AnimationClip - 动画片断
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于保存一个单独动画序列
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-03-14

#include "animation_clip.h"
#include <game/animation/animation.h>
#include <game/animation/armature.h>
#include <game/animation/avatar.h>
#include <graphics/imath.h>
#include <kits.h>
#include <klog.h>
#include <knumber.h>
#include <kstring.h>

using namespace DirectX;

namespace ifire::game {

AnimationEvent::AnimationEvent(const ikit::XmlNode& node, int _index)
    : index(_index)
    , name(node.Value())
    , clip(node("Clip"))
    , time(node("Time")) {}

AnimationClip::AnimationClip(Animation* parent, ikit::sstr _name)
    : animation_(parent)
    , name(_name)
    , is_master_(true)
    , master_clip_(nullptr) {}

AnimationClip::AnimationClip(Animation* parent, const ikit::XmlNode& node)
    : animation_(parent)
    , name(node("Name"))
    , master_name(node("Master"))
    , start_frame_(node("Start"))
    , end_frame_(node("End"))
    , is_master_(false)
    , master_clip_(nullptr) {}

void AnimationClip::SetMaster(AnimationClip* master_clip) {
  // 得到
  master_clip_ = master_clip;
  // 得到Fps
  frame_per_second_ = master_clip_->frame_per_second_;
  // 通过开始和结束计算Frame
  frame_count_ = end_frame_ - start_frame_;
  // 通过FPS计算总时长
  duration_ = frame_count_ / frame_per_second_;
}

AnimationClip::~AnimationClip() {
  for (auto& pair : channels_map_) {
    delete pair.second; // 释放 AnimationChannel 指针
  }
  channels_map_.clear(); // 清空 map
}

void AnimationClip::Read(const aiAnimation* anim) {
  // 配置名称
  name = ikit::get_string_uid(anim->mName.C_Str());

  // 配置时间
  frame_per_second_ = ikit::pick_default((float)anim->mTicksPerSecond, 25.0f);
  // 得到帧数，并且开始设置为0，结束设置为和帧数相同
  frame_count_ = (float)anim->mDuration;
  // 通过FPS计算总时长
  duration_ = frame_count_ / frame_per_second_;
  // 配置开始结束点
  start_frame_ = 0.0f;
  end_frame_ = frame_count_;

  // 给每一个Channel分配项目
  channels_map_.reserve(anim->mNumChannels);

  // 遍历Channel
  for (size_t j = 0; j < anim->mNumChannels; j++) {
    const aiNodeAnim* node_anim = anim->mChannels[j];
    auto channel = new AnimationChannel(this, node_anim->mNodeName.C_Str());

    // 加入到队列和MAP中
    channels_map_[channel->name.uid()] = channel;

    // 给每个项目预留位置
    channel->position_keys.reserve(node_anim->mNumPositionKeys);
    channel->rotation_keys.reserve(node_anim->mNumRotationKeys);
    channel->scale_keys.reserve(node_anim->mNumScalingKeys);

    for (size_t k = 0; k < node_anim->mNumPositionKeys; k++) {
      auto& key = node_anim->mPositionKeys[k];
      XMFLOAT3 pos = imath::ConvertVector3(key.mValue);
      if (animation_->IsScale()) {
        imath::Scale(pos, animation_->GetScaleFactor());
      }
      channel->position_keys.emplace_back(static_cast<float>(key.mTime), pos);
    }
    for (size_t k = 0; k < node_anim->mNumRotationKeys; k++) {
      auto& key = node_anim->mRotationKeys[k];
      channel->rotation_keys.emplace_back(
          static_cast<float>(key.mTime), imath::ConvertQuaternion(key.mValue));
    }
    for (size_t k = 0; k < node_anim->mNumScalingKeys; k++) {
      auto& key = node_anim->mScalingKeys[k];
      channel->scale_keys.emplace_back(
          static_cast<float>(key.mTime), imath::ConvertVector3(key.mValue));
    }
  }
}

XMMATRIX AnimationClip::CalcTransform(
    float frame, const AnimationChannel* channel) const {
  // 根据当前的Frame开始和结束设置，定位channel真实的frame
  // 在进行这个计算之前，首先在其它地方的计算（比如说根据LOOP循环），不会让值超过现在设计的Frame
  float fixed_frame = start_frame_ + frame;

  // 使用Channel计算
  return channel->CalcTransform(fixed_frame);
}

float AnimationClip::CalcFrame(float time_in_seconds, PlayMethod method) const {
  // 通过时间计算出frame
  float time_pin = time_in_seconds;

  if (method == PlayMethod::None) {
    // 如果超出限制，则使用最后一项
    if (time_pin > GetDuration()) {
      time_pin = GetDuration();
    }
  } else if (method == PlayMethod::Loop) {
    // 循环循环
    time_pin = fmod(time_pin, GetDuration());
  }

  float frame = time_pin * GetFps();

  // KLOG("Time:%f, Pin:%f, Duration:%f, Frame:%f, FrameCount:%f,
  // StartFrame:%f", time, time_pin,
  //     clip->GetDuration(), frame, clip->GetFrameCount(), clip->GetStart());

  // 通过Pin计算出应该有的帧数
  return frame;
}

const AnimationChannel* AnimationClip::FindChannel(ikit::sstr name) {
  // 如果拥有引用，则使用引用
  if (master_clip_) {
    return master_clip_->FindChannel(name);
  }

  auto it = channels_map_.find(name.uid());
  if (it != channels_map_.end()) {
    return it->second;
  } else {
    return nullptr;
  }
}

const AnimationChannel* AnimationClip::FindChannelAvatar(
    ikit::sstr name, Avatar* chara) {
  // 如果拥有引用，则使用引用
  if (master_clip_) {
    return master_clip_->FindChannelAvatar(name, chara);
  }

  // 获得名称代表的avatar
  auto bone_id = chara->FindId(name);
  if (bone_id == AVATAR_NONE_ID) {
    return nullptr;
  }

  auto bone = GetAvatar()->FindBone(bone_id);
  // 如果没有找到合适的Bone，则返回
  if (bone.empty()) {
    return nullptr;
  }

  // 根据名称查找
  return FindChannel(bone);
}

void AnimationClip::Print() {
  KLOG("\n---------------------------------------------");
  KLOG("Print AnimationClip: %s", ikit::get_chars(name));
  KLOG("Channels:");
  int c_index = 0;
  for (auto& e : channels_map_) {
    auto channel = e.second;
    KLOG("[%d]: %s", c_index++, ikit::get_chars(channel->name));
  }
  // 打印Avatar
  auto avatar = GetAnimation()->GetAvatar();
  if (avatar) {
    avatar->Print();
  }

  KLOG("---------------------------------------------");

  // 用于临时打印
  char position_buffer[100];
  char rotation_buffer[100];
  char scale_buffer[100];

  c_index = 0;
  for (auto& e : channels_map_) {
    auto channel = e.second;
    KLOG("-- %s[%d]: --", ikit::get_chars(channel->name), c_index++);

    int frame_count = (int)frame_count_;

    auto get_position = [channel](size_t frame, char* buffer) -> bool {
      for (size_t i = 0; i < channel->position_keys.size(); i++) {
        auto& key = channel->position_keys[i];
        if (static_cast<size_t>(key.time) == frame) {
          auto v = key.value;
          sprintf(buffer, "P[%.1f %.1f %.1f]", v.x, v.y, v.z);
          return true;
        }
      }
      sprintf(buffer, "P[           ]");
      return false;
    };
    auto get_rotation = [channel](size_t frame, char* buffer) -> bool {
      for (size_t i = 0; i < channel->rotation_keys.size(); i++) {
        auto& key = channel->rotation_keys[i];
        if (static_cast<size_t>(key.time) == frame) {
          XMFLOAT3 v = imath::QuaternionToEulerDegree(key.GetValue());
          sprintf(buffer, "R[%.1f %.1f %.1f]", v.x, v.y, v.z);
          return true;
        }
      }
      sprintf(buffer, "R[           ]");
      return false;
    };
    auto get_scale = [this, channel](size_t frame, char* buffer) -> bool {
      for (size_t i = 0; i < channel->scale_keys.size(); i++) {
        auto& key = channel->scale_keys[i];
        if (static_cast<size_t>(key.time) == frame) {
          auto v = key.value;
          sprintf(buffer, "S[%.1f %.1f %.1f]", v.x, v.y, v.z);
          return true;
        }
      }
      sprintf(buffer, "S[           ]");
      return false;
    };

    for (size_t i = 0; i <= frame_count; i++) {
      bool position_enable = get_position(i, position_buffer);
      bool rotation_enable = get_rotation(i, rotation_buffer);
      bool scale_enable = get_scale(i, scale_buffer);
      // 如果有一个命中，则显示
      if (position_enable || rotation_enable || scale_enable) {
        ikit::print("%03df: %s %s %s\n", i, position_buffer, rotation_buffer,
            scale_buffer);
      }
    }
  }

  KLOG("\n-- END --\n");
}

const Avatar* AnimationClip::GetAvatar() const {
  return animation_->GetAvatar();
}

bool AnimationClip::IsRootMotion() const { return animation_->IsRootMotion(); }

bool AnimationClip::IsHumanoid() const { return animation_->IsHumanoid(); }

bool AnimationClip::IsFlipRotation() const {
  return animation_->IsFlipRotation();
}

FMat AnimationClip::GetFlipRotation() const {
  return animation_->GetFlipRotation();
}

IAxis AnimationClip::GetRootMotionAxis() const {
  return animation_->GetRootMotionAxis();
}

void AnimationClip::Ready() { animation_->Ready(); }

} // namespace ifire::game
